//******************************************************************************
/**
 *
 * @file	slot_reel.c
 * @brief	ミニゲーム　スロット　リール制御
 * @author	iwasawa
 * @data	08.11.22
 *
 */
//******************************************************************************
#include "common.h"
#include "system/procsys.h"
#include "system/bmp_list.h"
#include "system/bmp_menu.h"
#include "system/arc_util.h"
#include "system/arc_tool.h"
#include "system/snd_tool.h"
#include "system/fontproc.h"
#include "system/pm_str.h"
#include "system/msgdata.h"
#include "system/buflen.h"
#include "system/window.h"
#include "system/brightness.h"
#include "system/clact_util.h"
#include "system/clact_tool.h"
#include "system/render_oam.h"
#include "system/palanm.h"
#include "system/wipe.h"
#include "system/window.h"
#include "system/font_arc.h"
#include "system/winframe.naix"
#include "gflib/touchpanel.h"
#include "gflib/strbuf_family.h"

#include "application/app_tool.h"

#include "slot_local.h"
#include "slot_sub.h"
#include "slot_reel.h"

//=====================================================================
//リテラル定義
//=====================================================================
//--------------------------------------------------------------
//	リール制御
//--------------------------------------------------------------
enum
{
	RC_NONE = 0,
	RC_SCROLL,
	RC_STOP_SET,
	RC_STOP,
	RC_STOP_SHAKE,
	RC_MAX,
};

typedef struct _LINE_TBL{
 u8	no,ofs;
}LINE_TBL;

//=====================================================================
//プロトタイプ定義
//=====================================================================
static void tcb_ReelControl( TCB_PTR tcb, void *work );
static int ReelControl_Non( REEL_CONTROL *reel, SLOTMAIN *wk );
static int ReelControl_Scroll( REEL_CONTROL *reel, SLOTMAIN *wk );
static int ReelControl_StopSet( REEL_CONTROL *reel, SLOTMAIN *wk );
static int ReelControl_Stop( REEL_CONTROL *reel, SLOTMAIN *wk );
static int ReelControl_StopShake( REEL_CONTROL *reel, SLOTMAIN *wk );

static void reel_Scroll(SLOTMAIN* wk,REEL_NUM no,int y);
static int reel_ScrollGrid(SLOTMAIN* wk,REEL_NUM no,int y);
static void reel_ActorMove( REEL_CONTROL* reel,SLOTMAIN *wk );

static REEL_PATTERN reel_PatternGet( SLOTMAIN *wk, REEL_NUM no, int offs );
static REEL_PATTERN reel_PatternGridGet( SLOTMAIN *wk, REEL_NUM no, int offs );
static int reel_PatternOffsetGet( SLOTMAIN* wk,REEL_NUM no, int view_ofs,REEL_PATTERN target);
static int reel_PullInTargetGet( SLOTMAIN* wk,REEL_NUM no,REEL_PATTERN target,u16 limit,int offs,int* buf);
static BOOL reel_IsHitLineComp( SLOTMAIN* wk,LINE_CODE code,REEL_PATTERN target,int num,int ofs_reel,int offs);
static BOOL reel_IsLineCompBonus( SLOTMAIN* wk,BONUS_CODE target,int num,int offs);
static int reel_CheckVLine( SLOTMAIN* wk,REEL_NUM no,REEL_PATTERN target,int offs,int* buf);
static int reel_IsReach( SLOTMAIN* wk, REEL_PATTERN target, int offs,int* list);
static REEL_PATTERN reel_GetTarget( int lot_basic );

static void cont_ReelControl1( SLOTMAIN* wk,REEL_CONTROL* reel);
static void cont_ReelControl2( SLOTMAIN* wk,REEL_CONTROL* reel);
static void cont_ReelControl3( SLOTMAIN* wk,REEL_CONTROL* reel);
static void cont_BonusReelControl( SLOTMAIN* wk,REEL_CONTROL* reel);
static int cont_ReelStopResultErrCheck( SLOTMAIN* wk,REEL_CONTROL* reel);

static int rc1_BonguriSlipOut( SLOTMAIN* wk );
static int rc1_BonguriSet( SLOTMAIN* wk ,REEL_PATTERN target);
static int rc1_AutoTargetSet( SLOTMAIN* wk, REEL_PATTERN target );
static int rc1_ViewHitCheck( SLOTMAIN* wk,REEL_NUM no,REEL_PATTERN target,u16* slip);
static int rc1_TargetPullIn( SLOTMAIN* wk, REEL_PATTERN target );
static int rc1_BonguriPullIn( SLOTMAIN* wk,REEL_NUM no,REEL_PATTERN target,int limit);

static int rc2_BonusReachSlipOut( SLOTMAIN* wk);
static int rc2_AutoReachSet( SLOTMAIN* wk,REEL_PATTERN target);
static int rc2_TargetReachPullIn( SLOTMAIN* wk,REEL_PATTERN target);

static int rc3_SlipOut( SLOTMAIN* wk );
static int rc3_AutoWinSet( SLOTMAIN *wk, REEL_PATTERN target);
static int rc3_TargetWinPullIn( SLOTMAIN *wk, REEL_PATTERN target);

static int brc_AutoTargetSet( SLOTMAIN* wk, REEL_CONTROL* reel );
static int brc_TargetPullIn( SLOTMAIN* wk, REEL_CONTROL* reel );

//=====================================================================
//データ定義
//=====================================================================

//--------------------------------------------------------------
///	リール制御テーブル
//--------------------------------------------------------------
static int (* const DATA_ReelControlTbl[RC_MAX])( REEL_CONTROL *reel, SLOTMAIN *wk ) =
{
	ReelControl_Non,
	ReelControl_Scroll,
	ReelControl_StopSet,
	ReelControl_Stop,
	ReelControl_StopShake,
};

///////////////////////////////////////////////////////////////////////
//=====================================================================
//グローバル関数定義
//=====================================================================
///////////////////////////////////////////////////////////////////////

/**
 *	@brief	リール位置初期化
 */
void SlotReel_PosInit(SLOTMAIN* wk)
{
	int i,gy;

	for( i = REEL_0; i < REEL_MAX; i++ ){
		if(wk->bonus_game_f){
			gy = DATA_BonusGameReelInitPos[wk->bonus_code];
		}else{
			gy = (int)gf_rand() % REEL_PT_MAX;
		}
		wk->reel_grid_y[i] = gy;
		wk->reel_dot_y[i] = REEL_SCROLL_MAX - REEL_PT_V_GD(gy);
		wk->reel_ofs_y[i] = 0;
	}
}

/*
 *	@brief	リールの初期描画状態セット
 */
void SlotReel_ViewInit(SLOTMAIN* wk)
{
	int i,j,idx;
	REEL_PATTERN	no;

	for(i = 0;i < REEL_LINE_Y;i++){
		for(j = 0;j < REEL_MAX;j++){
			idx = REEL_IDX(j,i);
			no = reel_PatternGet( wk, j, i);

			CATS_ObjectPosSet(wk->pAReel[idx],
				(ACT_REEL_OX*j)+ACT_REEL_PX ,(ACT_REEL_OY*i)+ACT_REEL_PY );

			CLACT_AnmChg( wk->pAReel[idx], ANM_REEL_IDX+no );
			CLACT_AnmFrameSet( wk->pAReel[idx], 0 );
		}
	}
}

//=====================================================================
///////////////////////////////////////////////////////////////////////
//リール制御系
///////////////////////////////////////////////////////////////////////
//=====================================================================
//--------------------------------------------------------------
/**
 * リール制御処理セット
 * @param	main	SLOTMAIN
 * @retval	nothing
 */
//--------------------------------------------------------------
void ReelControlSet( SLOTMAIN *wk )
{
	int i = 0;

	for(i = 0;i < REEL_MAX;i++){
		wk->reel[i].seq_no = 0;
		wk->reel[i].reel_no = i;
		wk->reel[i].smp = wk;
		wk->reel[i].tcb = TCB_Add(tcb_ReelControl,&wk->reel[i],TCBPRI_REEL_CONTROL0+i);
	}
	wk->reel_speed = 12;
}

//--------------------------------------------------------------
/**
 * リール制御処理削除
 * @param	main	SLOTMAIN
 * @retval	nothing
 */
//--------------------------------------------------------------
void ReelControlDelete( SLOTMAIN *wk )
{
	int i = 0;
	for(i = 0;i < REEL_MAX;i++){
		TCB_Delete(wk->reel[i].tcb);
	}
}

//--------------------------------------------------------------
/**
 * リール制御　回転する
 * @param	main	SLOTMAIN
 * @param	no		REEL_NUM
 * @retval	nothing
 */
//--------------------------------------------------------------
void ReelControlScrollSet( SLOTMAIN *wk, REEL_NUM no )
{
	wk->reel[no].seq_no = RC_SCROLL;
	wk->reel[no].scroll_flag = TRUE;
}

//--------------------------------------------------------------
/**
 * リール制御　抽選ターゲットをセット
 * @param	wk	SLOTMAIN
 * @retval	nothing
 */
//--------------------------------------------------------------
void ReelControlLotteryTargetSet( SLOTMAIN *wk )
{
	int i,target;

	if(wk->bonus_game_f){
		for(i = 0;i < REEL_MAX;i++)	{
			wk->reel[i].target_idx = (DATA_BonusGameReelInitPos[wk->lot_basic]+2)%REEL_PT_MAX;
			wk->reel[i].target = (9*wk->lot_basic)+REEL_CHIKO4+i;
		}
	}else{
		if(wk->lot_basic == PRIZE_BLANK){
			if(wk->lot_ex == PRIZE_ST_0){
				target = reel_GetTarget(SlotSub_Rand(2)+PRIZE_REG);
			}else{
				target = REEL_EMPTY;
			}
		}else{
			target = reel_GetTarget(wk->lot_basic);
		}
		for(i = 0;i < REEL_MAX;i++){
			wk->reel[i].target = target;
		}
	}
}

//--------------------------------------------------------------
/**
 * リール制御　停止する
 * @param	main	SLOTMAIN
 * @param	no		REEL_NUM
 * @retval	nothing
 */
//--------------------------------------------------------------
void ReelControlStopSet( SLOTMAIN *wk, REEL_NUM no )
{
	wk->reel[no].seq_no = RC_STOP_SET;
}

//--------------------------------------------------------------
/**
 * 当たり/リーチチェック
 * @param	main	SLOTMAIN
 * @retval	nothing
 */
//--------------------------------------------------------------
int ReelCheck( SLOTMAIN *wk ,REEL_CHECK_MODE mode, int* ret_b,LINE_CODE* hit_line )
{
	int i,j,target,code = 0;
	int num = 0;

	for(i = PRIZE_REPLAY;i <= PRIZE_BIG;i++){
		target = reel_GetTarget(i);
		if(i == PRIZE_BONG){
			if(mode == REEL_CHECK_REACH){
				continue;	//リーチチェックはしない
			}
			for(j = 0;j <= LINE_SIDE3;j++){
				if(reel_IsHitLineComp( wk, j, target, 1, REEL_1+mode, 0)){
					hit_line[num] = j;
					ret_b[num++] = i;
				}
			}
		}else{
			for(j = 0;j < LINE_CODE_MAX;j++){
				if(reel_IsHitLineComp( wk, j, target, 2+mode, REEL_1+mode,0)){
					hit_line[num] = j;
					ret_b[num++] = i;
				}
			}
		}
	}
	return num;
}

//--------------------------------------------------------------
/**
 * 当たり/リーチチェック  ボーナス
 * @param	main	SLOTMAIN
 * @retval	nothing
 */
//--------------------------------------------------------------
int ReelCheckBonus( SLOTMAIN *wk ,REEL_CHECK_MODE mode)
{
	if(reel_IsLineCompBonus( wk,wk->lot_basic,2+mode,0)){
		return wk->lot_basic;
	}
	return -1;
}

//--------------------------------------------------------------
/**
 * リールアクター制御メイン
 */
//--------------------------------------------------------------
void ReelActorMain( SLOTMAIN *wk )
{
	int i,no;

	for(no = 0;no < REEL_MAX;no++){
		if(!wk->reel[no].scroll_flag){
			continue;
		}
		reel_ActorMove( &wk->reel[no],wk );
	}
}

//--------------------------------------------------------------
/**
 * リール制御動作
 * @param	tcb		TCB_PTR
 * @param	wk		tcb work
 * @retval	nothing
 */
//--------------------------------------------------------------
static void tcb_ReelControl( TCB_PTR tcb, void *work )
{
	REEL_CONTROL *reel = (REEL_CONTROL*)work;
	SLOTMAIN *wk = reel->smp;

	while( DATA_ReelControlTbl[reel->seq_no](reel,wk) == TRUE ){};
}

//--------------------------------------------------------------
/**
 * リール制御　無し
 * @param	reel	REEL_CONTROL
 * @param	main	SLOTMAIN *
 * @retval	int		TRUE=再帰
 */
//--------------------------------------------------------------
static int ReelControl_Non( REEL_CONTROL *reel, SLOTMAIN *wk )
{
	return( FALSE );
}

//--------------------------------------------------------------
/**
 * リール制御　スクロール
 * @param	reel	REEL_CONTROL
 * @param	main	SLOTMAIN *
 * @retval	int		TRUE=再帰
 */
//--------------------------------------------------------------
static int ReelControl_Scroll( REEL_CONTROL *reel, SLOTMAIN *wk )
{
	reel_Scroll( wk, reel->reel_no, wk->reel_speed );
	return( FALSE );
}

/**
 *	リール制御　リールのスリップ幅チェック
 */
static BOOL reel_SlipGridCheck( REEL_CONTROL* reel, SLOTMAIN* wk)
{
	if(reel->slip_grid_y >= REEL_PT_MAX){
		return TRUE;	//一周以上するわけがない
	}
	if(wk->bonus_game_f){
		if((!wk->reel_auto[reel->reel_no]) && (reel->slip_grid_y > DATA_BonusGameReelPullInLimit[wk->reel_speed_id])){
			return TRUE;
		}
	}else{
		if(wk->lot_basic == PRIZE_BLANK && reel->reel_no == 2){
			return FALSE;	//必ずハズレにスリップリミットはない
		}
		if(wk->lot_ex == 0 && reel->reel_no < 2){
			return FALSE;	//自動揃え
		}
		if(wk->lot_ex == 1 && reel->slip_grid_y > PULLIN_LIM_N){
			return TRUE;	//目押し
		}
		if(wk->lot_ex != 2){
			return FALSE;
		}
		if(reel->reel_no == 0 && reel->slip_grid_y > PULLIN_LIM_N){
			return TRUE;	//１列目の目押し
		}
	}
	return FALSE;
}

//--------------------------------------------------------------
/**
 * リール制御　ストップセット
 * @param	reel	REEL_CONTROL
 * @param	main	SLOTMAIN *
 * @retval	int		TRUE=再帰
 */
//--------------------------------------------------------------
static int ReelControl_StopSet( REEL_CONTROL *reel, SLOTMAIN *wk )
{
	int no = reel->reel_no;

	if(wk->bonus_game_f){
		cont_BonusReelControl( wk,reel);

		if(reel_SlipGridCheck( reel, wk)){
#if 0
			GF_ASSERT_MSG(0,"BonusReelStopSetFail\nlot(%d:%d), rell = %d slip_grid = %d speed=%d\n",
							wk->lot_basic,wk->lot_ex,no,reel->slip_grid_y,wk->reel_speed_id);
#else
			OS_Printf("BonusReelStopSetFail\nlot(%d:%d), rell = %d slip_grid = %d speed=%d\n",
							wk->lot_basic,wk->lot_ex,no,reel->slip_grid_y,wk->reel_speed_id);
#endif
			if(reel->slip_grid_y >= REEL_PT_MAX){
				reel->slip_grid_y = 0;	//万が一のためのチェック
			}
		}
	}else{
		switch(no){
		case 0:
			cont_ReelControl1(wk,reel);
			break;
		case 1:
			cont_ReelControl2(wk,reel);
			break;
		case 2:
			cont_ReelControl3(wk,reel);
			break;
		}
		if(reel_SlipGridCheck( reel, wk)){
#if 0
			GF_ASSERT_MSG(0,"ReelStopSetFail lot(%d:%d), rell = %d slip_grid = %d\n",
							wk->lot_basic,wk->lot_ex,no,reel->slip_grid_y);
#else
			OS_Printf("ReelStopSetFail lot(%d:%d), rell = %d slip_grid = %d\n",
							wk->lot_basic,wk->lot_ex,no,reel->slip_grid_y);
#endif
			if(reel->slip_grid_y >= REEL_PT_MAX){
				reel->slip_grid_y = 0;	//万が一のためのチェック
			}
		}
	}
	reel->seq_no = RC_STOP;
	return( TRUE );
}

//--------------------------------------------------------------
/**
 * リール制御　ストップ
 * @param	reel	REEL_CONTROL
 * @param	main	SLOTMAIN *
 * @retval	int		TRUE=再帰
 */
//--------------------------------------------------------------
static int ReelControl_Stop( REEL_CONTROL *reel, SLOTMAIN *wk )
{
	int dif,speed;
	int no = reel->reel_no;
	BOOL move_f = FALSE;

	dif = wk->reel_dot_y[no] % REEL_PT_V_SIZE;

	if(dif){
		if(dif < wk->reel_speed){
			speed = dif;
		}else{
			speed = wk->reel_speed;
		}
		dif = reel_ScrollGrid(wk,no,speed);
		move_f = TRUE;
	}else if( reel->slip_grid_y){
		reel->slip_grid_y--;
		reel_Scroll( wk, no, wk->reel_speed );
		dif = wk->reel_dot_y[no] % REEL_PT_V_SIZE;
		move_f = TRUE;
	}
	if(dif == 0 && reel->slip_grid_y == 0){
		reel->slip_dot_y = 3;
		reel->frame = 0;
		reel->seq_no = RC_STOP_SHAKE;
		Snd_SePlay(SND_SLOT_REEL_STOP);

		if(move_f == FALSE){
			return TRUE;
		}
	}
	return( FALSE );
}

//--------------------------------------------------------------
/**
 * リール制御　ストップ時の振動
 * @param	reel	REEL_CONTROL
 * @param	main	SLOTMAIN *
 * @retval	int		TRUE=再帰
 */
//--------------------------------------------------------------
static int ReelControl_StopShake( REEL_CONTROL *reel, SLOTMAIN *wk )
{
	int dif,speed;
	int no = reel->reel_no;

	wk->reel_ofs_y[no] = reel->slip_dot_y;
	reel->slip_dot_y *= -1;

	if(reel->frame++ & 1){
		reel->slip_dot_y--;
	}
	if(reel->slip_dot_y == 0){
		reel->seq_no = RC_NONE;
		reel->scroll_flag = FALSE;
		wk->reel_ofs_y[no] = 0;
	}
	return( FALSE );
}

/**
 *	@brief	リールの回転
 */
static void reel_Scroll(SLOTMAIN* wk,REEL_NUM no,int y)
{
	if(wk->reel_reverce[no]){
		wk->reel_dot_y[no] -= y;
		if(wk->reel_dot_y[no] < 0){
			wk->reel_dot_y[no] += REEL_SCROLL_MAX;
		}
	}else{
		wk->reel_dot_y[no] += y;
		wk->reel_dot_y[no] %= REEL_SCROLL_MAX;	//回り込み
	}
	//グリッドオフセット計算
	wk->reel_grid_y[no] = REEL_PT_MAX-REEL_PT_V_DG(wk->reel_dot_y[no]);
}

/**
 *	@brief	リールがグリッドぴったりになるまで回転
 *
 *	@return	グリッドぴったりに動くまでの差分
 */
static int reel_ScrollGrid(SLOTMAIN* wk,REEL_NUM no,int y)
{
	int diff;

	diff = wk->reel_dot_y[no] % REEL_PT_V_SIZE;

	if(diff){	//余りがある場合
		if(diff < y){
			y = diff;
		}
		reel_Scroll( wk, no ,y );
		diff = wk->reel_dot_y[no] % REEL_PT_V_SIZE;
	}
	return diff;
}

//--------------------------------------------------------------
/**
 * リールアクター制御メイン
 * @param	no		REEL_NUM
 * @param	offs	オフセット　グリッド単位
 * @retval	REEL_PATTERN	REEL_PATTERN
 */
//--------------------------------------------------------------
static void reel_ActorMove( REEL_CONTROL* reel,SLOTMAIN *wk )
{
	int i,no,idx;
	int y,pat;

	no = reel->reel_no;
	for(i = 0;i < REEL_LINE_Y;i++){
		idx = i*REEL_MAX+no;

		y = wk->reel_dot_y[no]+(i*REEL_PT_V_SIZE);
		y %=(REEL_PT_V_MAX);

		CATS_ObjectPosSet(wk->pAReel[idx],
			(ACT_REEL_OX*no)+ACT_REEL_PX ,y + ACT_REEL_PY + wk->reel_ofs_y[no]);

		pat = reel_PatternGet(wk, no, REEL_PT_V_DG(y)) + ANM_REEL_IDX;

		if( pat != CLACT_AnmGet(wk->pAReel[idx]) ){
			CLACT_AnmChg( wk->pAReel[idx], pat );
			CLACT_AnmFrameSet( wk->pAReel[idx], 0 );
		}
	}
}

//==============================================================================
//	パーツ
//==============================================================================
//--------------------------------------------------------------
/**
 * リール絵柄取得
 * @param	no		REEL_NUM
 * @param	offs	オフセット　グリッド単位
 * @retval	REEL_PATTERN	REEL_PATTERN
 */
//--------------------------------------------------------------
static REEL_PATTERN reel_PatternGet( SLOTMAIN *wk, REEL_NUM no, int offs )
{
	int pos = (wk->reel_grid_y[no] + offs) % REEL_PT_MAX;

	if( pos < 0 ){
		pos = REEL_PT_MAX + pos;
	}

	if(wk->bonus_game_f){
		return ( DATA_ReelPatternBonus[no][pos]);
	}
	return( DATA_ReelPatternNormal[no][pos] );
}

//--------------------------------------------------------------
/**
 * リール絵柄取得　グリッド補正アリ
 * @param	no		REEL_NUM
 * @param	offs	オフセット　グリッド単位
 * @retval	REEL_PATTERN	REEL_PATTERN
 */
//--------------------------------------------------------------
static REEL_PATTERN reel_PatternGridGet( SLOTMAIN *wk, REEL_NUM no, int offs )
{
	int diff = 0;

	if( (wk->reel_dot_y[no] % REEL_PT_V_SIZE) ){
		if(!wk->reel_reverce[no]){
			diff--;
		}
	}

	return( reel_PatternGet(wk,no,offs+diff) );
}

/**
 *	指定のパターンが現在位置からオフセットいくつの場所にあるかを返す
 */
static int reel_PatternOffsetGet( SLOTMAIN* wk,REEL_NUM no, int view_ofs,REEL_PATTERN target)
{
	REEL_PATTERN pat;
	int offs = 1;

	do{
		offs--;
		pat  = reel_PatternGridGet( wk, no, offs+REEL_VIEW_OFS+view_ofs);
	}while(pat != target);

	return -(offs);
}

/**
 *	ボーナスゲームで指定のパターンが現在位置からオフセットいくつの場所にあるかを返す
 */
static int breel_PatternOffsetGet( SLOTMAIN* wk,REEL_NUM no, REEL_PATTERN target)
{
	REEL_PATTERN pat;
	int offs = -1,dir;

	if(wk->reel_reverce[no]){
		dir = 1;
	}else{
		dir = -1;
	}
	do{
		offs++;
		pat  = reel_PatternGridGet( wk, no, (offs*dir)+REEL_BONUS_VIEW_OFS);
	}while(pat != target);

	return offs;
}

/**
 *	@brief	引き込み候補を汎用バッファに取得
 *
 */
static int reel_PullInTargetGet( SLOTMAIN* wk,REEL_NUM no,REEL_PATTERN target,u16 limit,int offs,int* buf)
{
	int i,num = 0;
	int pat;

	for(i = 0;i < limit;i++){
		pat = reel_PatternGridGet( wk, no, -i+offs );
		if(pat != target){
			continue;
		}
		buf[num++] = i+REEL_VIEW_OFS;
	}
	return num;
}

/**
 *	@brief	ラインチェック
 */
static BOOL reel_IsHitLineComp( SLOTMAIN* wk,LINE_CODE code,REEL_PATTERN target,int num,int ofs_reel,int offs)
{
	int i,pat,ct;

	static const LINE_TBL line_tbl[LINE_CODE_MAX][REEL_MAX] = {
		{ {REEL_0,1},{REEL_1,1},{REEL_2,1} },	//SIDE1	上
		{ {REEL_0,2},{REEL_1,2},{REEL_2,2} },	//SIDE2	真ん中
		{ {REEL_0,3},{REEL_1,3},{REEL_2,3} },	//SIDE3	下
		{ {REEL_0,1},{REEL_1,2},{REEL_2,3} },	//CROSS1
		{ {REEL_0,3},{REEL_1,2},{REEL_2,1} },	//CROSS2
	};
	ct = 0;
	for(i = 0;i < num;i++){
		if(i == ofs_reel){	//オフセット適用するリールナンバー
			pat = reel_PatternGridGet( wk, line_tbl[code][i].no, line_tbl[code][i].ofs+offs );
		}else{
			pat = reel_PatternGridGet( wk, line_tbl[code][i].no, line_tbl[code][i].ofs );
		}

		if(pat != target){
			continue;
		}
		if(++ct == num){
			return TRUE;
		}
	}
	return FALSE;
}

/**
 *	@brief	ラインチェック ボーナス
 */
static BOOL reel_IsLineCompBonus( SLOTMAIN* wk,BONUS_CODE target,int num,int offs)
{
	int i,pat,ct;

	static const LINE_TBL line_tbl[REEL_MAX] = {
		{REEL_0,2},{REEL_1,2},{REEL_2,2}	//SIDE2	真ん中
	};
	ct = 0;
	for(i = 0;i < num;i++){
		if(i == (num-1)){
			pat = reel_PatternGridGet( wk, line_tbl[i].no, line_tbl[i].ofs+offs );
		}else{
			pat = reel_PatternGridGet( wk, line_tbl[i].no, line_tbl[i].ofs );
		}
		if(pat != REEL_CHIKO4+i+(target*9)){
			continue;
		}
		if(++ct == num){
			return TRUE;
		}
	}
	return FALSE;
}

/**
 *	@brief	縦ラインチェック
 */
static int reel_CheckVLine( SLOTMAIN* wk,REEL_NUM no,REEL_PATTERN target,int offs,int* buf)
{
	int i,num = 0,pat;

	for(i = 0;i < REEL_VIEW_MAX;i++){
		pat = reel_PatternGridGet( wk, no, REEL_VIEW_OFS+i+offs );
		if(pat != target){
			continue;
		}
		if(buf != NULL){
			buf[num] = i;
		}
		num++;
	}
	return num;
}

/**
 *	@brief	目押しの有効範囲から指定ラインに引き込むターゲットオフセットを取得
 */
static int reel_GetPullInTargetOffset( SLOTMAIN* wk, REEL_NUM no,REEL_PATTERN target,int limit,int v_line)
{
	int num,t_ofs;

	num = reel_PullInTargetGet( wk,no,target,limit,v_line,wk->i_buf);
	if(num == 0){
		return 0;
	}
	return wk->i_buf[SlotSub_Rand(num)];
}

/**
 *	@brief	指定パターンのリーチが成立するかどうかを返す
 */
static int reel_IsReach( SLOTMAIN* wk, REEL_PATTERN target, int offs,int* list)
{
	int i,num = 0;

	for(i = 0;i < LINE_CODE_MAX;i++){
		if(reel_IsHitLineComp( wk, i, target, 2, REEL_1,offs)){
			if(list != NULL){
				list[num] = i;
			}
			num++;
		}
	}
	return num;
}

/**
 *	@brief	指定の役が成立しているかどうかチェック
 */
static BOOL reel_IsWin( SLOTMAIN* wk,REEL_PATTERN target,int offs)
{
	int i;

	if(target == REEL_BONG){
		for(i = 0;i <= LINE_SIDE3;i++){
			if(reel_IsHitLineComp( wk, i, target, 1, REEL_2, offs)){
				return TRUE;
			}
		}
		return FALSE;
	}
	for(i = 0;i < LINE_CODE_MAX;i++){
		if(reel_IsHitLineComp( wk, i, target, 3, REEL_2,offs)){
			return TRUE;
		}
	}
	return FALSE;
}

/**
 *	@brief	基本抽選結果からターゲットパターンを取得
 */
static REEL_PATTERN reel_GetTarget( int lot_basic )
{
	return lot_basic-1;
}

/**
 *	@brief	１列目制御
 */
static void cont_ReelControl1( SLOTMAIN* wk,REEL_CONTROL* reel)
{
	int hit;
	u16 rnd,slip[REEL_VIEW_MAX];
	int no = reel->reel_no;

	hit = FALSE;
	if(wk->lot_basic == PRIZE_BLANK){
		if(wk->lot_ex != PRIZE_ST_0){	//単チェリーはずし
			reel->slip_grid_y = rc1_BonguriSlipOut(wk);
			return;
		}
	}else if(wk->lot_basic == PRIZE_BONG){	//ぼんぐり当選
		reel->slip_grid_y = rc1_BonguriSet( wk ,reel->target);
		return;
	}
	//チェリー外しつつ、ターゲットを引っ張る
	if(wk->lot_ex == PRIZE_ST_0){
		reel->slip_grid_y = rc1_AutoTargetSet( wk, reel->target );
	}else{
		reel->slip_grid_y = rc1_TargetPullIn( wk, reel->target );
	}
}

/**
 *	@brief	二列目制御
 */
static void cont_ReelControl2( SLOTMAIN* wk,REEL_CONTROL* reel)
{
	REEL_PATTERN target;

	if(wk->lot_basic == PRIZE_BLANK){
		switch(wk->lot_ex){
		case PRIZE_ST_0:
			reel->slip_grid_y = rc2_AutoReachSet(wk,reel->target);
			return;
		case PRIZE_ST_1:
			reel->slip_grid_y = rc2_BonusReachSlipOut(wk);
			return;
		case PRIZE_ST_2:
		default:
			reel->slip_grid_y = 0;
			return;
		}
	}else if(wk->lot_basic == PRIZE_BONG){
		reel->slip_grid_y = rc2_BonusReachSlipOut(wk);
		return;
	}else if(wk->lot_ex == PRIZE_ST_0){
		reel->slip_grid_y = rc2_AutoReachSet(wk,reel->target);
		return;
	}
	reel->slip_grid_y = rc2_TargetReachPullIn( wk,reel->target);
}

/**
 *	@brief	最終的に止まるリールの目をチェック
 */
static int cont_ReelStopResultErrCheck( SLOTMAIN* wk,REEL_CONTROL* reel)
{
	int i,num = 0;
	u8 line[LINE_CODE_MAX];

	if(wk->lot_basic == PRIZE_BLANK){
		return FALSE;
	}
	MI_CpuClear8(line,sizeof(u8)*LINE_CODE_MAX);
	for(i = REEL_REPLAY ;i <= REEL_SEVEN;i++ ){
		if(reel_IsWin(wk,i,-reel->slip_grid_y)){
			line[num++] = i+1;
		}
	}
	if(num == 1){
		if(line[0] != wk->lot_basic){
			OS_Printf("基本抽選外を引いた basic = %d win = %d\n",wk->lot_basic,line[0]);
			return TRUE;
		}
	}else if(num > 1){
		OS_Printf("%d個当選した %d,%d\n",num,line[0],line[1]);
		return TRUE;
	}
	return FALSE;
}

/**
 *	@brief	三列目制御
 */
static void cont_ReelControl3( SLOTMAIN* wk,REEL_CONTROL* reel)
{
	REEL_PATTERN target;

	if(wk->lot_basic == PRIZE_BLANK || wk->lot_basic == PRIZE_BONG){
		reel->slip_grid_y = rc3_SlipOut(wk);
	}else if(wk->lot_basic == PRIZE_BIG || wk->lot_basic == PRIZE_REG || wk->lot_ex != PRIZE_ST_0){
		reel->slip_grid_y = rc3_TargetWinPullIn( wk,reel->target);
	}else{
		reel->slip_grid_y = rc3_AutoWinSet(wk,reel->target);
	}
	//リールの揃い目チェック
	if(cont_ReelStopResultErrCheck(wk,reel)){
		reel->slip_grid_y = rc3_SlipOut(wk);
	}
}

/**
 *	@brief	ボーナスゲーム制御
 */
static void cont_BonusReelControl( SLOTMAIN* wk,REEL_CONTROL* reel)
{
	int hit;
	u16 rnd,slip[REEL_VIEW_MAX];
	int no = reel->reel_no;

	if(wk->reel_auto[no]){
		reel->slip_grid_y = brc_AutoTargetSet( wk, reel);
	}else{
		reel->slip_grid_y = brc_TargetPullIn( wk, reel );
	}
}

/**
 *	@brief	１列目制御　単チェリー外し
 */
static int rc1_BonguriSlipOut( SLOTMAIN* wk )
{
	int num,offs = 1;
	int list[REEL_VIEW_MAX];

	do{
		offs--;
		num = reel_CheckVLine( wk,REEL_0,REEL_BONG,offs,list);
	}while(num);
	return -offs;
}

/**
 *	@brief	１列目制御　単チェリーヒット
 */
static int rc1_BonguriSet( SLOTMAIN* wk ,REEL_PATTERN target)
{
	int offs,hit = FALSE;
	u16	slip[REEL_VIEW_MAX];

	hit = rc1_ViewHitCheck( wk,REEL_0,target,slip);

	if(hit){
		return 0;
	}
	if(wk->lot_ex == PRIZE_ST_0){	//オート
		return slip[SlotSub_Rand(REEL_VIEW_MAX)];
	}else{	//引き込み
		return rc1_BonguriPullIn( wk,REEL_0,target, PULLIN_LIM_N);
	}
}

/**
 *	@brief	１列目制御　オートセット(チェリー以外)
 */
static int rc1_AutoTargetSet( SLOTMAIN* wk, REEL_PATTERN target )
{
	int i,offs,flag;
	int t_num = 0,num = 0;
	int t_list[REEL_VIEW_MAX],list[REEL_VIEW_MAX];

	t_num = 0;
	offs = 1;
	flag = FALSE;
	do{
		t_num = reel_CheckVLine( wk,REEL_0,target,--offs,t_list);

		if(t_num == 0){	//ターゲットが見つからない
			continue;
		}
		//ターゲットが見つかった時ボングリチェック
		num = reel_CheckVLine( wk,REEL_0,REEL_BONG,offs,NULL);

		if(num == 0){
			flag = TRUE;
		}
	}while(!flag);

	if(offs == 0){
		return 0;	//動かさない
	}
	//近いほう優先で抽選
	if(t_list[t_num-1] == 2){
		return -offs;
	}

	list[0] = offs;
	num = 1;
	for(i = 1;i < (3-t_list[t_num-1]);i++){
		if(reel_CheckVLine( wk,REEL_0,REEL_BONG,offs-i,NULL) == 0){
			list[num++] = offs-i;
		}
	}
	return -(list[SlotSub_Rand(num)]);
}

/**
 *	@brief	１列目制御　目押し(チェリー以外)
 */
static int rc1_TargetPullIn( SLOTMAIN* wk, REEL_PATTERN target )
{
	int i,j,l_num,t_num,num,offs;
	int t_list[REEL_VIEW_MAX],list[PULLIN_LIM_N];

	static const search[6] = {1,2,3,3,2,1};

	//現在をチェック
	l_num = 0;
	t_num = reel_CheckVLine( wk,REEL_0,target,0,t_list);
	if(t_num){
		//ターゲットが見つかった時ボングリチェック
		if(reel_CheckVLine( wk,REEL_0,REEL_BONG,0,NULL) == 0){
			return 0;
		}
		for(i = 0;i < t_num;i++){
			if(t_list[i] < 2){	//下段は無理なので2以下を候補にする
				list[l_num++] = -t_list[i];
			}
		}
	}
	//引き込みチェック
	l_num += reel_PullInTargetGet( wk,REEL_0,target,PULLIN_LIM_N,0,&list[l_num]);

	num = 0;
	for(i = 0;i < l_num;i++){
		if(list[i] < 1){
			for(j = 0;j < search[list[i]+1];j++){
				if(reel_CheckVLine( wk,REEL_0,REEL_BONG,-(j+1),NULL) == 0){
					wk->i_buf[num++] = j+1;
				}
			}
		}else{
			for(j = 0;j < search[list[i]+1];j++){
				if(reel_CheckVLine( wk,REEL_0,REEL_BONG,-(j+list[i]),NULL) == 0){
					wk->i_buf[num++] = j+list[i];
				}
			}
		}
	}
	if(num == 0){	//引き込めるものがない
		return rc1_BonguriSlipOut( wk );
	}
	return wk->i_buf[SlotSub_Rand(num)];
}

/**
 *	@brief	１列目制御 View内に有効コマがあるかチェック
 */
static int rc1_ViewHitCheck( SLOTMAIN* wk,REEL_NUM no,REEL_PATTERN target,u16* slip)
{
	int i,hit,tmp;

	hit = 0;
	for(i = 0;i < REEL_VIEW_MAX;i++){
		tmp = reel_PatternOffsetGet(wk,no,i,target);
		if(tmp == 0){
			hit = i+1;
		}
		if(slip != NULL){
			slip[i] = tmp;
		}
	}
#ifdef DEBUG_ONLY_FOR_iwasawa
	if(slip != 0){
		IWASAWA_PRINT("ViewHit %d %d %d\n",slip[0],slip[1],slip[2]);
	}
#endif
	return hit;
}

/**
 *	@brief	１列目制御 有効コマがない場合に、ターゲットをランダム位置に引き込むオフセットを返す
 */
static int rc1_BonguriPullIn( SLOTMAIN* wk,REEL_NUM no,REEL_PATTERN target,int limit)
{
	u16 rnd;
	int num,t_ofs;
	static const l_tbl[] = {3,3,2,1,1};

	num = reel_PullInTargetGet( wk,no,target,limit,0,wk->i_buf);
	if(num == 0){
		return 0;
	}
	rnd = SlotSub_Rand(num);
	t_ofs = wk->i_buf[rnd];

	return SlotSub_Rand(l_tbl[t_ofs])+t_ofs;
}

/**
 *	@brief	二列目制御	ボーナスリーチにならないようにスリップさせる
 */
static int rc2_BonusReachSlipOut( SLOTMAIN* wk)
{
	int offs = 1;
	int flag;

	do{
		flag = 0;
		offs--;
		flag += reel_IsReach( wk, REEL_SEVEN, offs, NULL);
		flag += reel_IsReach( wk, REEL_BALL, offs, NULL);
	}while(flag);

	return -offs;
}

/**
 *	@brief	二列目制御　オートでリーチを揃える
 */
static int rc2_AutoReachSet( SLOTMAIN* wk,REEL_PATTERN target)
{
	int offs = 1;
	int flag = 0,num = 0;
	u16 rnd,line;

	//１列目のターゲット位置を取得
	num = reel_CheckVLine( wk,REEL_0,target,0,wk->i_buf);
	if(num == 0){
		GF_ASSERT_MSG(0,"２列目オート １列目に有効コマ%dがありません\n",target);
		return 0;
	}
	rnd = SlotSub_Rand(num);
	line = wk->i_buf[rnd];
	if(SlotSub_Rand(2)){
		if(line == LINE_SIDE1){
			line = LINE_CROSS1;
		}else if(line == LINE_SIDE3){
			line = LINE_CROSS2;
		}
	}
	do{
		offs--;
		flag = reel_IsHitLineComp(wk,line,target,2,REEL_1,offs);
	}while(!flag);

	offs *= -1;
	return offs;
}

/**
 *	@brief	二列目制御　目押しでリーチを狙わせる
 */
static int rc2_TargetReachPullIn( SLOTMAIN* wk,REEL_PATTERN target)
{
	int i,num,l_num,tmp;
	int flag,offs;
	int t_list[REEL_VIEW_MAX],list[5];
	flag = 0;
	offs = 1;

	//１列目が成立しているか？
	num = reel_CheckVLine( wk,REEL_0,target,0,t_list);
	if(num == 0){	//非成立
		if(wk->lot_ex == PRIZE_ST_1){
			return rc2_BonusReachSlipOut( wk );
		}
		return 0;
	}
	if(reel_IsReach( wk, target, 0, NULL)){	//既に成立か？
		return 0;
	}

	l_num = 0;
	for(i = 0;i < num;i++){
		switch(t_list[i]){
		case 0:
			tmp = reel_GetPullInTargetOffset( wk, REEL_1, target,PULLIN_LIM_N,0);
			if(tmp != 0){
				list[l_num++] = tmp;
			}
			tmp = reel_GetPullInTargetOffset( wk, REEL_1, target,PULLIN_LIM_N,1);
			if(tmp != 0){
				list[l_num++] = tmp;
			}
			break;
		case 1:
			tmp = reel_GetPullInTargetOffset( wk, REEL_1, target,PULLIN_LIM_N,1);
			if(tmp != 0){
				list[l_num++] = tmp;
			}
			break;
		case 2:
			tmp = reel_GetPullInTargetOffset( wk, REEL_1, target,PULLIN_LIM_N,1);
			if(tmp != 0){
				list[l_num++] = tmp;
			}
			tmp = reel_GetPullInTargetOffset( wk, REEL_1, target,PULLIN_LIM_N,2);
			if(tmp != 0){
				list[l_num++] = tmp;
			}
			break;
		}
	}
	//２列目制御はノーマル時のみなので、ボーナスゲームは気にせず条件チェック
	if(l_num == 0){
		if(wk->lot_ex == PRIZE_ST_1){
			return rc2_BonusReachSlipOut( wk );
		}
		return 0;
	}
	return list[SlotSub_Rand(l_num)];
}


/**
 *	@brief	三列目制御　役(チェリー以外)を外すようにスリップ
 */
static int rc3_SlipOut( SLOTMAIN* wk )
{
	int i,offs = 1;
	int flag;

	do{
		flag = FALSE;
		offs--;
		for(i = REEL_REPLAY;i <= REEL_SEVEN;i++){
			if(i == REEL_BONG){
				continue;	//チェリー当選はチェックしなくていい
			}
			if(	reel_IsWin( wk,i,offs )){
				flag = TRUE;
			}
		}
	}while(flag);
	return -offs ;
}

/*
 *	@brief	三列目制御　オートで役を成立させる
 */
static int rc3_AutoWinSet( SLOTMAIN *wk, REEL_PATTERN target)
{
	int i,offs = 1;
	int flag;

	do{
		offs--;
		flag = reel_IsWin( wk,target, offs );
	}while(!flag);

	return -offs;
}

/*
 *	@brief	三列目制御　目押しで役を成立させる
 */
static int rc3_TargetWinPullIn( SLOTMAIN *wk, REEL_PATTERN target)
{
	int i,l_num,tmp;
	int limit,num;
	int line[LINE_CODE_MAX],list[5];

	//リーチが成立しているか？
	num = reel_IsReach( wk, target, 0, line);
	if(num == 0){
		return 0;
	}
	//既に成立しているか？
	if(reel_IsWin( wk, target, 0)){
		return 0;
	}

	limit = PULLIN_LIM_N;
	l_num = 0;
	for(i = 0;i < num;i++){
		switch(line[num]){
		case LINE_SIDE1:
		case LINE_CROSS1:
			tmp = reel_GetPullInTargetOffset( wk, REEL_2, target,limit,0);
			if(tmp != 0){
				list[l_num++] = tmp;
			}
			break;
		case LINE_SIDE2:
			tmp = reel_GetPullInTargetOffset( wk, REEL_2, target,limit,1);
			if(tmp != 0){
				list[l_num++] = tmp;
			}
			break;
		case LINE_SIDE3:
		case LINE_CROSS2:
			tmp = reel_GetPullInTargetOffset( wk, REEL_2, target,limit,2);
			if(tmp != 0){
				list[l_num++] = tmp;
			}
			break;
		}
	}
	if(l_num == 0){
		return 0;
	}
	return list[SlotSub_Rand(l_num)];
}

/**
 *	@brief	ボーナスリール制御　オートセット
 */
static int brc_AutoTargetSet( SLOTMAIN* wk, REEL_CONTROL* reel )
{
	return breel_PatternOffsetGet( wk, reel->reel_no, reel->target);
}

/**
 *	@brief	ボーナスリール制御	引き込み
 */
static int brc_TargetPullIn( SLOTMAIN* wk, REEL_CONTROL* reel )
{
	int offs,i;

	offs = breel_PatternOffsetGet( wk, reel->reel_no, reel->target);

	if(offs <= DATA_BonusGameReelPullInLimit[wk->reel_speed_id]){
		return offs;
	}
	if(reel->reel_no != REEL_2){
		return 0;
	}
	for(i = 0;i < BONUS_CODE_MAX;i++){
		if(wk->lot_basic == i){
			continue;
		}
		if(reel_IsLineCompBonus( wk,i,3,0)){
			return SlotSub_Rand(2)+1;
		}
	}
	return 0;
}
